home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Games / MAME / src / timer.c < prev    next >
C/C++ Source or Header  |  2000-03-15  |  21KB  |  876 lines

  1. /***************************************************************************
  2.  
  3.   timer.c
  4.  
  5.   Functions needed to generate timing and synchronization between several
  6.   CPUs.
  7.  
  8.   Changes 2/27/99:
  9.       - added some rounding to the sorting of timers so that two timers
  10.           allocated to go off at the same time will go off in the order
  11.           they were allocated, without concern for floating point rounding
  12.           errors (thanks Juergen!)
  13.       - fixed a bug where the base_time was not updated when a CPU was
  14.           suspended, making subsequent calls to getabsolutetime() return an
  15.           incorrect time (thanks Nicola!)
  16.       - changed suspended CPUs so that they don't eat their timeslice until
  17.           all other CPUs have used up theirs; this allows a slave CPU to
  18.           trigger a higher priority CPU in the middle of the timeslice
  19.       - added the ability to call timer_reset() on a oneshot or pulse timer
  20.           from within that timer's callback; in this case, the timer won't
  21.           get removed (oneshot) or won't get reprimed (pulse)
  22.  
  23.   Changes 12/17/99 (HJB):
  24.     - added overclocking factor and functions to set/get it at runtime.
  25.  
  26.   Changes 12/23/99 (HJB):
  27.     - added burn() function pointer to tell CPU cores when we want to
  28.       burn cycles, because the cores might need to adjust internal
  29.       counters or timers.
  30.  
  31. ***************************************************************************/
  32.  
  33. #include "cpuintrf.h"
  34. #include "driver.h"
  35. #include "timer.h"
  36.  
  37. #include <stdarg.h>
  38.  
  39.  
  40. #define VERBOSE 0
  41.  
  42. #define MAX_TIMERS 256
  43.  
  44.  
  45. /*
  46.  *        internal timer structures
  47.  */
  48. typedef struct timer_entry
  49. {
  50.     struct timer_entry *next;
  51.     struct timer_entry *prev;
  52.     void (*callback)(int);
  53.     int callback_param;
  54.     int enabled;
  55.     double period;
  56.     double start;
  57.     double expire;
  58. } timer_entry;
  59.  
  60. typedef struct
  61. {
  62.     int *icount;
  63.     void (*burn)(int cycles);
  64.     int index;
  65.     int suspended;
  66.     int trigger;
  67.     int nocount;
  68.     int lost;
  69.     double time;
  70.     double sec_to_cycles;
  71.     double cycles_to_sec;
  72.     double overclock;
  73. } cpu_entry;
  74.  
  75.  
  76. /* conversion constants */
  77. double cycles_to_sec[MAX_CPU];
  78. double sec_to_cycles[MAX_CPU];
  79.  
  80. /* list of per-CPU timer data */
  81. static cpu_entry cpudata[MAX_CPU+1];
  82. static cpu_entry *lastcpu;
  83. static cpu_entry *activecpu;
  84. static cpu_entry *last_activecpu;
  85.  
  86. /* list of active timers */
  87. static timer_entry timers[MAX_TIMERS];
  88. static timer_entry *timer_head;
  89. static timer_entry *timer_free_head;
  90.  
  91. /* other internal states */
  92. static double base_time;
  93. static double global_offset;
  94. static timer_entry *callback_timer;
  95. static int callback_timer_modified;
  96.  
  97. /* prototypes */
  98. static int pick_cpu(int *cpu, int *cycles, double expire);
  99.  
  100. #if VERBOSE
  101. static void verbose_print(char *s, ...);
  102. #endif
  103.  
  104. /*
  105.  *        return the current absolute time
  106.  */
  107. INLINE double getabsolutetime(void)
  108. {
  109.     if (activecpu && (*activecpu->icount + activecpu->lost) > 0)
  110.         return base_time - ((double)(*activecpu->icount + activecpu->lost) * activecpu->cycles_to_sec);
  111.     else
  112.         return base_time;
  113. }
  114.  
  115.  
  116. /*
  117.  *        adjust the current CPU's timer so that a new event will fire at the right time
  118.  */
  119. INLINE void timer_adjust(timer_entry *timer, double time, double period)
  120. {
  121.     int newicount, diff;
  122.  
  123.     /* compute a new icount for the current CPU */
  124.     if (period == TIME_NOW)
  125.         newicount = 0;
  126.     else
  127.         newicount = (int)((timer->expire - time) * activecpu->sec_to_cycles) + 1;
  128.  
  129.     /* determine if we're scheduled to run more cycles */
  130.     diff = *activecpu->icount - newicount;
  131.  
  132.     /* if so, set the new icount and compute the amount of "lost" time */
  133.     if (diff > 0)
  134.     {
  135.         activecpu->lost += diff;
  136.         if (activecpu->burn)
  137.             (*activecpu->burn)(diff);  /* let the CPU burn the cycles */
  138.         else
  139.             *activecpu->icount = newicount;  /* CPU doesn't care */
  140.     }
  141. }
  142.  
  143.  
  144. /*
  145.  *        allocate a new timer
  146.  */
  147. INLINE timer_entry *timer_new(void)
  148. {
  149.     timer_entry *timer;
  150.  
  151.     /* remove an empty entry */
  152.     if (!timer_free_head)
  153.         return NULL;
  154.     timer = timer_free_head;
  155.     timer_free_head = timer->next;
  156.  
  157.     return timer;
  158. }
  159.  
  160.  
  161. /*
  162.  *        insert a new timer into the list at the appropriate location
  163.  */
  164. INLINE void timer_list_insert(timer_entry *timer)
  165. {
  166.     double expire = timer->enabled ? timer->expire : TIME_NEVER;
  167.     timer_entry *t, *lt = NULL;
  168.  
  169.     /* loop over the timer list */
  170.     for (t = timer_head; t; lt = t, t = t->next)
  171.     {
  172.         /* if the current list entry expires after us, we should be inserted before it */
  173.         /* note that due to floating point rounding, we need to allow a bit of slop here */
  174.         /* because two equal entries -- within rounding precision -- need to sort in */
  175.         /* the order they were inserted into the list */
  176.         if ((t->expire - expire) > TIME_IN_NSEC(1))
  177.         {
  178.             /* link the new guy in before the current list entry */
  179.             timer->prev = t->prev;
  180.             timer->next = t;
  181.  
  182.             if (t->prev)
  183.                 t->prev->next = timer;
  184.             else
  185.                 timer_head = timer;
  186.             t->prev = timer;
  187.             return;
  188.         }
  189.     }
  190.  
  191.     /* need to insert after the last one */
  192.     if (lt)
  193.         lt->next = timer;
  194.     else
  195.         timer_head = timer;
  196.     timer->prev = lt;
  197.     timer->next = NULL;
  198. }
  199.  
  200.  
  201. /*
  202.  *        remove a timer from the linked list
  203.  */
  204. INLINE void timer_list_remove(timer_entry *timer)
  205. {
  206.     /* remove it from the list */
  207.     if (timer->prev)
  208.         timer->prev->next = timer->next;
  209.     else
  210.         timer_head = timer->next;
  211.     if (timer->next)
  212.         timer->next->prev = timer->prev;
  213. }
  214.  
  215.  
  216. /*
  217.  *        initialize the timer system
  218.  */
  219. void timer_init(void)
  220. {
  221.     cpu_entry *cpu;
  222.     int i;
  223.  
  224.     /* keep a local copy of how many total CPU's */
  225.     lastcpu = cpudata + cpu_gettotalcpu() - 1;
  226.  
  227.     /* we need to wait until the first call to timer_cyclestorun before using real CPU times */
  228.     base_time = 0.0;
  229.     global_offset = 0.0;
  230.     callback_timer = NULL;
  231.     callback_timer_modified = 0;
  232.  
  233.     /* reset the timers */
  234.     memset(timers, 0, sizeof(timers));
  235.  
  236.     /* initialize the lists */
  237.     timer_head = NULL;
  238.     timer_free_head = &timers[0];
  239.     for (i = 0; i < MAX_TIMERS-1; i++)
  240.         timers[i].next = &timers[i+1];
  241.  
  242.     /* reset the CPU timers */
  243.     memset(cpudata, 0, sizeof(cpudata));
  244.     activecpu = NULL;
  245.     last_activecpu = lastcpu;
  246.  
  247.     /* compute the cycle times */
  248.     for (cpu = cpudata, i = 0; cpu <= lastcpu; cpu++, i++)
  249.     {
  250.         /* make a pointer to this CPU's interface functions */
  251.         cpu->icount = cpuintf[Machine->drv->cpu[i].cpu_type & ~CPU_FLAGS_MASK].icount;
  252.         cpu->burn = cpuintf[Machine->drv->cpu[i].cpu_type & ~CPU_FLAGS_MASK].burn;
  253.  
  254.         /* get the CPU's overclocking factor */
  255.         cpu->overclock = cpuintf[Machine->drv->cpu[i].cpu_type & ~CPU_FLAGS_MASK].overclock;
  256.  
  257.         /* everyone is active but suspended by the reset line until further notice */
  258.         cpu->suspended = SUSPEND_REASON_RESET;
  259.  
  260.         /* set the CPU index */
  261.         cpu->index = i;
  262.  
  263.         /* compute the cycle times */
  264.         cpu->sec_to_cycles = sec_to_cycles[i] = cpu->overclock * Machine->drv->cpu[i].cpu_clock;
  265.         cpu->cycles_to_sec = cycles_to_sec[i] = 1.0 / sec_to_cycles[i];
  266.     }
  267. }
  268.  
  269. /*
  270.  *        get overclocking factor for a CPU
  271.  */
  272. double timer_get_overclock(int cpunum)
  273. {
  274.     cpu_entry *cpu = &cpudata[cpunum];
  275.     return cpu->overclock;
  276. }
  277.  
  278. /*
  279.  *        set overclocking factor for a CPU
  280.  */
  281. void timer_set_overclock(int cpunum, double overclock)
  282. {
  283.     cpu_entry *cpu = &cpudata[cpunum];
  284.     cpu->overclock = overclock;
  285.     cpu->sec_to_cycles = sec_to_cycles[cpunum] = cpu->overclock * Machine->drv->cpu[cpunum].cpu_clock;
  286.     cpu->cycles_to_sec = cycles_to_sec[cpunum] = 1.0 / sec_to_cycles[cpunum];
  287. }
  288.  
  289. /*
  290.  *        allocate a pulse timer, which repeatedly calls the callback using the given period
  291.  */
  292. void *timer_pulse(double period, int param, void (*callback)(int))
  293. {
  294.     double time = getabsolutetime();
  295.     timer_entry *timer;
  296.  
  297.     /* allocate a new entry */
  298.     timer = timer_new();
  299.     if (!timer)
  300.         return NULL;
  301.  
  302.     /* fill in the record */
  303.     timer->callback = callback;
  304.     timer->callback_param = param;
  305.     timer->enabled = 1;
  306.     timer->period = period;
  307.  
  308.     /* compute the time of the next firing and insert into the list */
  309.     timer->start = time;
  310.     timer->expire = time + period;
  311.     timer_list_insert(timer);
  312.  
  313.     /* if we're supposed to fire before the end of this cycle, adjust the counter */
  314.     if (activecpu && timer->expire < base_time)
  315.         timer_adjust(timer, time, period);
  316.  
  317.     #if VERBOSE
  318.         verbose_print("T=%.6g: New pulse=%08X, period=%.6g\n", time + global_offset, timer, period);
  319.     #endif
  320.  
  321.     /* return a handle */
  322.     return timer;
  323. }
  324.  
  325.  
  326. /*
  327.  *        allocate a one-shot timer, which calls the callback after the given duration
  328.  */
  329. void *timer_set(double duration, int param, void (*callback)(int))
  330. {
  331.     double time = getabsolutetime();
  332.     timer_entry *timer;
  333.  
  334.     /* allocate a new entry */
  335.     timer = timer_new();
  336.     if (!timer)
  337.         return NULL;
  338.  
  339.     /* fill in the record */
  340.     timer->callback = callback;
  341.     timer->callback_param = param;
  342.     timer->enabled = 1;
  343.     timer->period = 0;
  344.  
  345.     /* compute the time of the next firing and insert into the list */
  346.     timer->start = time;
  347.     timer->expire = time + duration;
  348.     timer_list_insert(timer);
  349.  
  350.     /* if we're supposed to fire before the end of this cycle, adjust the counter */
  351.     if (activecpu && timer->expire < base_time)
  352.         timer_adjust(timer, time, duration);
  353.  
  354.     #if VERBOSE
  355.         verbose_print("T=%.6g: New oneshot=%08X, duration=%.6g\n", time + global_offset, timer, duration);
  356.     #endif
  357.  
  358.     /* return a handle */
  359.     return timer;
  360. }
  361.  
  362.  
  363. /*
  364.  *        reset the timing on a timer
  365.  */
  366. void timer_reset(void *which, double duration)
  367. {
  368.     double time = getabsolutetime();
  369.     timer_entry *timer = which;
  370.  
  371.     /* compute the time of the next firing */
  372.     timer->start = time;
  373.     timer->expire = time + duration;
  374.  
  375.     /* remove the timer and insert back into the list */
  376.     timer_list_remove(timer);
  377.     timer_list_insert(timer);
  378.  
  379.     /* if we're supposed to fire before the end of this cycle, adjust the counter */
  380.     if (activecpu && timer->expire < base_time)
  381.         timer_adjust(timer, time, duration);
  382.  
  383.     /* if this is the callback timer, mark it modified */
  384.     if (timer == callback_timer)
  385.         callback_timer_modified = 1;
  386.  
  387.     #if VERBOSE
  388.         verbose_print("T=%.6g: Reset %08X, duration=%.6g\n", time + global_offset, timer, duration);
  389.     #endif
  390. }
  391.  
  392.  
  393. /*
  394.  *        remove a timer from the system
  395.  */
  396. void timer_remove(void *which)
  397. {
  398.     timer_entry *timer = which;
  399.  
  400.     /* remove it from the list */
  401.     timer_list_remove(timer);
  402.  
  403.     /* free it up by adding it back to the free list */
  404.     timer->next = timer_free_head;
  405.     timer_free_head = timer;
  406.  
  407.     #if VERBOSE
  408.         verbose_print("T=%.6g: Removed %08X\n", getabsolutetime() + global_offset, timer);
  409.     #endif
  410. }
  411.  
  412.  
  413. /*
  414.  *        enable/disable a timer
  415.  */
  416. int timer_enable(void *which, int enable)
  417. {
  418.     timer_entry *timer = which;
  419.     int old;
  420.  
  421.     #if VERBOSE
  422.         if (enable) verbose_print("T=%.6g: Enabled %08X\n", getabsolutetime() + global_offset, timer);
  423.         else verbose_print("T=%.6g: Disabled %08X\n", getabsolutetime() + global_offset, timer);
  424.     #endif
  425.  
  426.     /* set the enable flag */
  427.     old = timer->enabled;
  428.     timer->enabled = enable;
  429.  
  430.     /* remove the timer and insert back into the list */
  431.     timer_list_remove(timer);
  432.     timer_list_insert(timer);
  433.  
  434.     return old;
  435. }
  436.  
  437.  
  438. /*
  439.  *        return the time since the last trigger
  440.  */
  441. double timer_timeelapsed(void *which)
  442. {
  443.     double time = getabsolutetime();
  444.     timer_entry *timer = which;
  445.  
  446.     return time - timer->start;
  447. }
  448.  
  449.  
  450. /*
  451.  *        return the time until the next trigger
  452.  */
  453. double timer_timeleft(void *which)
  454. {
  455.     double time = getabsolutetime();
  456.     timer_entry *timer = which;
  457.  
  458.     return timer->expire - time;
  459. }
  460.  
  461.  
  462. /*
  463.  *        return the current time
  464.  */
  465. double timer_get_time(void)
  466. {
  467.     return global_offset + getabsolutetime();
  468. }
  469.  
  470.  
  471. /*
  472.  *        return the time when this timer started counting
  473.  */
  474. double timer_starttime(void *which)
  475. {
  476.     timer_entry *timer = which;
  477.     return global_offset + timer->start;
  478. }
  479.  
  480.  
  481. /*
  482.  *        return the time when this timer will fire next
  483.  */
  484. double timer_firetime(void *which)
  485. {
  486.     timer_entry *timer = which;
  487.     return global_offset + timer->expire;
  488. }
  489.  
  490.  
  491. /*
  492.  *        begin CPU execution by determining how many cycles the CPU should run
  493.  */
  494. int timer_schedule_cpu(int *cpu, int *cycles)
  495. {
  496.     double end;
  497.  
  498.     /* then see if there are any CPUs that aren't suspended and haven't yet been updated */
  499.     if (pick_cpu(cpu, cycles, timer_head->expire))
  500.         return 1;
  501.  
  502.     /* everyone is up-to-date; expire any timers now */
  503.     end = timer_head->expire;
  504.     while (timer_head->expire <= end)
  505.     {
  506.         timer_entry *timer = timer_head;
  507.  
  508.         /* the base time is now the time of the timer */
  509.         base_time = timer->expire;
  510.  
  511.         #if VERBOSE
  512.             verbose_print("T=%.6g: %08X fired (exp time=%.6g)\n", getabsolutetime() + global_offset, timer, timer->expire + global_offset);
  513.         #endif
  514.  
  515.         /* set the global state of which callback we're in */
  516.         callback_timer_modified = 0;
  517.         callback_timer = timer;
  518.  
  519.         /* call the callback */
  520.         if (timer->callback)
  521.         {
  522.             profiler_mark(PROFILER_TIMER_CALLBACK);
  523.             (*timer->callback)(timer->callback_param);
  524.             profiler_mark(PROFILER_END);
  525.         }
  526.  
  527.         /* clear the callback timer global */
  528.         callback_timer = NULL;
  529.  
  530.         /* reset or remove the timer, but only if it wasn't modified during the callback */
  531.         if (!callback_timer_modified)
  532.         {
  533.             if (timer->period)
  534.             {
  535.                 timer->start = timer->expire;
  536.                 timer->expire += timer->period;
  537.  
  538.                 timer_list_remove(timer);
  539.                 timer_list_insert(timer);
  540.             }
  541.             else
  542.                 timer_remove(timer);
  543.         }
  544.     }
  545.  
  546.     /* reset scheduling so it starts with CPU 0 */
  547.     last_activecpu = lastcpu;
  548.  
  549. #ifdef MAME_DEBUG
  550. {
  551.     extern int debug_key_delay;
  552.     debug_key_delay = 0x7ffe;
  553. }
  554. #endif
  555.  
  556.     /* go back to scheduling */
  557.     return pick_cpu(cpu, cycles, timer_head->expire);
  558. }
  559.  
  560.  
  561. /*
  562.  *        end CPU execution by updating the number of cycles the CPU actually ran
  563.  */
  564. void timer_update_cpu(int cpunum, int ran)
  565. {
  566.     cpu_entry *cpu = cpudata + cpunum;
  567.  
  568.     /* update the time if we haven't been suspended */
  569.     if (!cpu->suspended)
  570.     {
  571.         cpu->time += (double)(ran - cpu->lost) * cpu->cycles_to_sec;
  572.         cpu->lost = 0;
  573.     }
  574.  
  575.     #if VERBOSE
  576.         verbose_print("T=%.6g: CPU %d finished (net=%d)\n", cpu->time + global_offset, cpunum, ran - cpu->lost);
  577.     #endif
  578.  
  579.     /* time to renormalize? */
  580.     if (cpu->time >= 1.0)
  581.     {
  582.         timer_entry *timer;
  583.         double one = 1.0;
  584.         cpu_entry *c;
  585.  
  586.         #if VERBOSE
  587.             verbose_print("T=%.6g: Renormalizing\n", cpu->time + global_offset);
  588.         #endif
  589.  
  590.         /* renormalize all the CPU timers */
  591.         for (c = cpudata; c <= lastcpu; c++)
  592.             c->time -= one;
  593.  
  594.         /* renormalize all the timers' times */
  595.         for (timer = timer_head; timer; timer = timer->next)
  596.         {
  597.             timer->start -= one;
  598.             timer->expire -= one;
  599.         }
  600.  
  601.         /* renormalize the global timers */
  602.         global_offset += one;
  603.     }
  604.  
  605.     /* now stop counting cycles */
  606.     base_time = cpu->time;
  607.     activecpu = NULL;
  608. }
  609.  
  610.  
  611. /*
  612.  *        suspend a CPU but continue to count time for it
  613.  */
  614. void timer_suspendcpu(int cpunum, int suspend, int reason)
  615. {
  616.     cpu_entry *cpu = cpudata + cpunum;
  617.     int nocount = cpu->nocount;
  618.     int old = cpu->suspended;
  619.  
  620.     #if VERBOSE
  621.         if (suspend) verbose_print("T=%.6g: Suspending CPU %d\n", getabsolutetime() + global_offset, cpunum);
  622.         else verbose_print("T=%.6g: Resuming CPU %d\n", getabsolutetime() + global_offset, cpunum);
  623.     #endif
  624.  
  625.     /* mark the CPU */
  626.     if (suspend)
  627.         cpu->suspended |= reason;
  628.     else
  629.         cpu->suspended &= ~reason;
  630.     cpu->nocount = 0;
  631.  
  632.     /* if this is the active CPU and we're halting, stop immediately */
  633.     if (activecpu && cpu == activecpu && !old && cpu->suspended)
  634.     {
  635.         #if VERBOSE
  636.             verbose_print("T=%.6g: Reset ICount\n", getabsolutetime() + global_offset);
  637.         #endif
  638.  
  639.         /* set the CPU's time to the current time */
  640.         cpu->time = base_time = getabsolutetime();    /* ASG 990225 - also set base_time */
  641.         cpu->lost = 0;
  642.  
  643.         /* no more instructions */
  644.         if (cpu->burn)
  645.             (*cpu->burn)(*cpu->icount); /* let the CPU burn the cycles */
  646.         else
  647.             *cpu->icount = 0;    /* CPU doesn't care */
  648.     }
  649.  
  650.     /* else if we're unsuspending a CPU, reset its time */
  651.     else if (old && !cpu->suspended && !nocount)
  652.     {
  653.         double time = getabsolutetime();
  654.  
  655.         /* only update the time if it's later than the CPU's time */
  656.         if (time > cpu->time)
  657.             cpu->time = time;
  658.         cpu->lost = 0;
  659.  
  660.         #if VERBOSE
  661.             verbose_print("T=%.6g: Resume time\n", cpu->time + global_offset);
  662.         #endif
  663.     }
  664. }
  665.  
  666.  
  667.  
  668. /*
  669.  *        hold a CPU and don't count time for it
  670.  */
  671. void timer_holdcpu(int cpunum, int hold, int reason)
  672. {
  673.     cpu_entry *cpu = cpudata + cpunum;
  674.  
  675.     /* same as suspend */
  676.     timer_suspendcpu(cpunum, hold, reason);
  677.  
  678.     /* except that we don't count time */
  679.     if (hold)
  680.         cpu->nocount = 1;
  681. }
  682.  
  683.  
  684.  
  685. /*
  686.  *        query if a CPU is suspended or not
  687.  */
  688. int timer_iscpususpended(int cpunum, int reason)
  689. {
  690.     cpu_entry *cpu = cpudata + cpunum;
  691.     return (cpu->suspended & reason) && !cpu->nocount;
  692. }
  693.  
  694.  
  695.  
  696. /*
  697.  *        query if a CPU is held or not
  698.  */
  699. int timer_iscpuheld(int cpunum, int reason)
  700. {
  701.     cpu_entry *cpu = cpudata + cpunum;
  702.     return (cpu->suspended & reason) && cpu->nocount;
  703. }
  704.  
  705.  
  706.  
  707. /*
  708.  *        suspend a CPU until a specified trigger condition is met
  709.  */
  710. void timer_suspendcpu_trigger(int cpunum, int trigger)
  711. {
  712.     cpu_entry *cpu = cpudata + cpunum;
  713.  
  714.     #if VERBOSE
  715.         verbose_print("T=%.6g: CPU %d suspended until %d\n", getabsolutetime() + global_offset, cpunum, trigger);
  716.     #endif
  717.  
  718.     /* suspend the CPU immediately if it's not already */
  719.     timer_suspendcpu(cpunum, 1, SUSPEND_REASON_TRIGGER);
  720.  
  721.     /* set the trigger */
  722.     cpu->trigger = trigger;
  723. }
  724.  
  725.  
  726.  
  727. /*
  728.  *        hold a CPU and don't count time for it
  729.  */
  730. void timer_holdcpu_trigger(int cpunum, int trigger)
  731. {
  732.     cpu_entry *cpu = cpudata + cpunum;
  733.  
  734.     #if VERBOSE
  735.         verbose_print("T=%.6g: CPU %d held until %d\n", getabsolutetime() + global_offset, cpunum, trigger);
  736.     #endif
  737.  
  738.     /* suspend the CPU immediately if it's not already */
  739.     timer_holdcpu(cpunum, 1, SUSPEND_REASON_TRIGGER);
  740.  
  741.     /* set the trigger */
  742.     cpu->trigger = trigger;
  743. }
  744.  
  745.  
  746.  
  747. /*
  748.  *        generates a trigger to unsuspend any CPUs waiting for it
  749.  */
  750. void timer_trigger(int trigger)
  751. {
  752.     cpu_entry *cpu;
  753.  
  754.     /* cause an immediate resynchronization */
  755.     if (activecpu)
  756.     {
  757.         int left = *activecpu->icount;
  758.         if (left > 0)
  759.         {
  760.             activecpu->lost += left;
  761.             if (activecpu->burn)
  762.                 (*activecpu->burn)(left); /* let the CPU burn the cycles */
  763.             else
  764.                 *activecpu->icount = 0; /* CPU doesn't care */
  765.         }
  766.     }
  767.  
  768.     /* look for suspended CPUs waiting for this trigger and unsuspend them */
  769.     for (cpu = cpudata; cpu <= lastcpu; cpu++)
  770.     {
  771.         if (cpu->suspended && cpu->trigger == trigger)
  772.         {
  773.             #if VERBOSE
  774.                 verbose_print("T=%.6g: CPU %d triggered\n", getabsolutetime() + global_offset, cpu->index);
  775.             #endif
  776.  
  777.             timer_suspendcpu(cpu->index, 0, SUSPEND_REASON_TRIGGER);
  778.             cpu->trigger = 0;
  779.         }
  780.     }
  781. }
  782.  
  783.  
  784. /*
  785.  *        pick the next CPU to run
  786.  */
  787. static int pick_cpu(int *cpunum, int *cycles, double end)
  788. {
  789.     cpu_entry *cpu = last_activecpu;
  790.  
  791.     /* look for a CPU that isn't suspended and hasn't run its full timeslice yet */
  792.     do
  793.     {
  794.         /* wrap around */
  795.         cpu += 1;
  796.         if (cpu > lastcpu)
  797.             cpu = cpudata;
  798.  
  799.         /* if this CPU is suspended, just bump its time */
  800.         if (cpu->suspended)
  801.         {
  802.             /* ASG 990225 - defer this update until the slice has finished */
  803. /*            if (!cpu->nocount)
  804.             {
  805.                 cpu->time = end;
  806.                 cpu->lost = 0;
  807.             }*/
  808.         }
  809.  
  810.         /* if this CPU isn't suspended and has time left.... */
  811.         else if (cpu->time < end)
  812.         {
  813.             /* mark the CPU active, and remember the CPU number locally */
  814.             activecpu = last_activecpu = cpu;
  815.  
  816.             /* return the number of cycles to execute and the CPU number */
  817.             *cpunum = cpu->index;
  818.             *cycles = (int)((double)(end - cpu->time) * cpu->sec_to_cycles);
  819.  
  820.             if (*cycles > 0)
  821.             {
  822.                 #if VERBOSE
  823.                     verbose_print("T=%.6g: CPU %d runs %d cycles\n", cpu->time + global_offset, *cpunum, *cycles);
  824.                 #endif
  825.  
  826.                 /* remember the base time for this CPU */
  827.                 base_time = cpu->time + ((double)*cycles * cpu->cycles_to_sec);
  828.  
  829.                 /* success */
  830.                 return 1;
  831.             }
  832.         }
  833.     }
  834.     while (cpu != last_activecpu);
  835.  
  836.     /* ASG 990225 - bump all suspended CPU times after the slice has finished */
  837.     for (cpu = cpudata; cpu <= lastcpu; cpu++)
  838.         if (cpu->suspended && !cpu->nocount)
  839.         {
  840.             cpu->time = end;
  841.             cpu->lost = 0;
  842.         }
  843.  
  844.     /* failure */
  845.     return 0;
  846. }
  847.  
  848.  
  849.  
  850. /*
  851.  *        debugging
  852.  */
  853. #if VERBOSE
  854.  
  855. #ifdef macintosh
  856. #undef printf
  857. #endif
  858.  
  859. static void verbose_print(char *s, ...)
  860. {
  861.     va_list ap;
  862.  
  863.     va_start(ap, s);
  864.  
  865.     #if (VERBOSE == 1)
  866.         if (errorlog) vfprintf(errorlog, s, ap);
  867.     #else
  868.         vprintf(s, ap);
  869.         fflush(NULL);
  870.     #endif
  871.  
  872.     va_end(ap);
  873. }
  874.  
  875. #endif
  876.